# -*- coding:utf-8 -*-
"""
管理页面组件

"""

import copy
import os
import logging
from datetime import datetime

from PyQt5.QtCore import Qt, QTimer
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import QWidget, QFileDialog, QListWidgetItem, QApplication
from PyQt5.QtGui import QPixmap

from LibrarySystem.config.config import Config
from LibrarySystem.repositories.sqlite3.models.book_models import Book
from LibrarySystem.services.ServicesHelper import ServicesHelper

from LibrarySystem.views.ui.AdminWidget import Ui_AdminWidget
from LibrarySystem.views.components.q_signals import q_signals
from LibrarySystem.views.components.custom_figure import CustomFigure
from LibrarySystem.views.components.book_detail import BookDetail
from LibrarySystem.views.components.messager import Messager

module_logger = logging.getLogger("logger")


class AdminWidget(QWidget, Ui_AdminWidget):
    """
    @ClassName：AdminWidget
    @Description：本项目只创建一个 AdminWidget 窗口实例，
    其中包含所有管理页面，通过左侧菜单栏进行切换页面
    负责用户在管理界面进行各种操作，操作结果以弹窗信息提示。
    @Author：锦沐Python
    """
    def __init__(self):
        super().__init__()
        self.config = Config()
        self.messager = Messager()
        self.setupUi(self)
        if self.config.Admin_QSS:
            self.setStyleSheet(self.config.Admin_QSS)
        self.ui_init()

    def ui_init(self):
        """
        将管理页面内所有按钮点击事件绑定到相关处理函数，
        创建并初始化统计界面的四个图表, 实际使用了3个，剩余的可以进行其他拓展
        """
        """菜单跳转按钮"""
        self.record_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(0))
        self.update_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(1))
        self.search_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(2))
        self.stats_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(3))
        self.user_button.clicked.connect(lambda: self.stackedWidget.setCurrentIndex(4))

        """图书录入"""
        # 数据暂存变量
        self.record_book = Book()
        self.record_file_button.clicked.connect(self.record_saveImage)
        self.record_ok_button.clicked.connect(self.record_ok)
        self.record_clear_button.clicked.connect(self.record_clear)
        self.pixmap = None

        """图书更新"""
        # 数据暂存变量
        self.update_book = Book()
        self.update_file_button.clicked.connect(self.update_saveImage)
        self.update_borrowed_by_select.addItems(["未出借", "已出借"])
        self.update_borrowed_by_select.setCurrentIndex(0)
        self.update_ok_button.clicked.connect(self.update_ok)
        self.update_del_button.clicked.connect(self.update_del)
        self.update_check_button.clicked.connect(self.update_check)

        """图书查询"""
        # 数据暂存变量
        self.query_book_list = []
        self.query_search_button.clicked.connect(self.query_books)
        self.query_rank_select.addItems(["热度升序", "热度降序", "记录日期升序", "数量升序", "数量降序", "已借出的", "未借出的"])
        self.query_rank_select.currentIndexChanged.connect(self.query_rank)

        q_signals.ListWidgetRefrsh_Signal.connect(self.query_books)

        """图书统计"""
        self.stats_search_button.clicked.connect(self.stats_search)
        self.stats_show_all_button.clicked.connect(self.stats_show_all)
        self.stats_out_imgs_button.clicked.connect(self.stats_out_img)

        # 创建四个统计图并添加到对应的位置，
        # 221表示：两行两列，从第一行开始从左到右，依次排序的第一个位置
        self.fig_221 = CustomFigure(name="fig_1")
        self.fig_222 = CustomFigure(name="fig_2")
        self.fig_223 = CustomFigure(name="fig_3")
        self.fig_224 = CustomFigure(name="fig_4")

        fig_layout_221 = QtWidgets.QVBoxLayout()
        fig_layout_221.addWidget(self.fig_221)
        self.figure_view_221.setLayout(fig_layout_221)

        fig_layout_222 = QtWidgets.QVBoxLayout()
        fig_layout_222.addWidget(self.fig_222)
        self.figure_view_222.setLayout(fig_layout_222)

        fig_layout_223 = QtWidgets.QVBoxLayout()
        fig_layout_223.addWidget(self.fig_223)
        self.figure_view_223.setLayout(fig_layout_223)

        fig_layout_224 = QtWidgets.QVBoxLayout()
        fig_layout_224.addWidget(self.fig_224)
        self.figure_view_224.setLayout(fig_layout_224)

        # 隐藏画布
        self.figure_view_221.hide()
        self.figure_view_222.hide()
        self.figure_view_223.hide()
        self.figure_view_224.hide()

        """用户信息"""
        self.user_role_select.addItems(["user", "admin"])
        self.user_role_select.setCurrentIndex(0)
        self.user_update_button.clicked.connect(self.user_update)
        self.user_deregister_button.clicked.connect(self.user_del)

        q_signals.LoginUserInfo_Signal.connect(self.get_user_info)

    def get_user_info(self):
        """
        获取登录用户信息，将信息显示到用户管理界面
        """
        (flag, login_user) = ServicesHelper.get_role_service().get_current_user()
        if flag is False:
            msg = login_user
            self.messager.show_error(f"{msg},即将退出程序")
            QTimer.singleShot(3000, QApplication.quit)
            return

        # 检查权限是否通过，严格来说，将其内部组件删除才是最保险的，但这里简单演示即可
        if ServicesHelper.get_role_service().check_permissions([0, 1, 2, 3, 4])[0]:
            self.record_button.show()
            self.update_button.show()
        elif ServicesHelper.get_role_service().check_permissions([2, 3, 4])[0]:
            self.record_button.hide()
            self.update_button.hide()
        else:
            self.messager.show_error("没有权限操作，即将退出界面")
            # 关闭程序
            QTimer.singleShot(3000, QApplication.quit)

        # 用户信息显示
        self.user_name_edit.setText(login_user.user_name)
        self.user_id_edit.setText(login_user.user_id)
        self.user_email_edit.setText(login_user.email)
        self.user_role_select.setCurrentText(login_user.role)

    """#添加图书界面"""
    def record_saveImage(self):
        """
        保存上传的 图片，将选择的图片显示
        """
        # 打开文件夹
        file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "*.jpg;;*.png")
        if file_path == "":
            self.messager.show_info("图片未选择")
            return

        try:
            # 显示图片
            pixmap = QPixmap(file_path)
            pixmap = pixmap.scaled(self.record_book_img.size(), Qt.IgnoreAspectRatio)
            self.record_book_img.setPixmap(pixmap)

            # 保存图片为 png 格式
            img_name = f"{datetime.now().timestamp()}.png"
            save_path = self.config.BOOK_IMG_SAVE_PATH + f"\\{img_name}"
            pixmap.save(save_path)

            # 显示保存路径
            self.record_file_name.setText(save_path)
            self.record_book.img_url = self.config.BOOK_IMG_SAVE_DIR + f"\\{img_name}"
            self.messager.show_info(f"上传图片成功")

        except Exception as e:
            module_logger.error(f"上传图片失败:{e}")
            self.messager.show_error(f"上传图片失败:{e}")

    def record_ok(self):
        """
         保存图书至数据库
        :return:
        """
        try:
            self.record_book.book_name = self.record_book_name_edit.text()
            self.record_book.ISBN = self.record_ISBN_edit.text()
            self.record_book.category = self.record_category_edit.text()
            self.record_book.author = self.record_author_edit.text()
            self.record_book.publisher = self.record_publisher_edit.text()
            self.record_book.publication_date = self.record_publication_date_edit.text()
            self.record_book.record_time = datetime.now().strftime("%Y.%m.%d %H:%M:%S")
            # 自动生成唯一ID,防止冲突
            self.record_book.book_id = self.record_book.create_id()

            # 保存图书到数据库
            (flag, msg) = ServicesHelper.get_record_service().record_book(self.record_book)
            if flag:
                self.messager.show_info(msg)
            else:
                self.messager.show_error(msg)

        except Exception as e:
            module_logger.info(f"录入图书失败:{e}")
            self.messager.show_error(f"录入图书失败:{e}")

    def record_clear(self):
        """
        清空添加图书界面输入框内容
        """
        self.record_book = Book()
        self.record_book_name_edit.setText("")
        self.record_ISBN_edit.setText("")
        self.record_category_edit.setText("")
        self.record_publisher_edit.setText("")
        self.record_file_name.setText("")
        self.record_author_edit.setText("")
        self.record_book_img.clear()

    """ 图书更新界面"""
    def update_saveImage(self):
        """
        保存上传的图片，并更新图片路径
        """
        # 选择图片
        file_path, _ = QFileDialog.getOpenFileName(self, "选择图片", "", "*.jpg;;*.png")
        if file_path == "":
            self.messager.show_info("图片未选择")
            return

        try:
            # 显示图片
            pixmap = QPixmap(file_path)
            pixmap = pixmap.scaled(self.update_book_img.size(), Qt.IgnoreAspectRatio)
            self.update_book_img.setPixmap(pixmap)

            # 保存图片
            img_name = f"{datetime.now().timestamp()}.png"
            save_path = self.config.BOOK_IMG_SAVE_PATH + f"\\{img_name}"
            pixmap.save(save_path)

            # 显示保存路径
            self.update_file_name.setText(save_path)
            self.update_book.img_url = self.config.BOOK_IMG_SAVE_DIR + f"\\{img_name}"
            self.messager.show_info(f"上传图片成功")

        except Exception as e:
            module_logger.error(f"上传图片失败:{e}")
            self.messager.show_error(f"上传图片失败:{e}")

    def update_check(self):
        """
        根据图书 id 查找书籍
        :return:
        """
        try:
            book_id = self.update_book_id_edit.text()

            # 获取书籍
            (flag ,book) = ServicesHelper.get_update_service().get_book_by_id(book_id)
            if flag is False:
                msg = book
                self.messager.show_error(msg)
                return

            # 更新书籍 信息
            self.update_book = book
            self.update_book_name_edit.setText(self.update_book.book_name)
            self.update_ISBN_edit.setText(self.update_book.ISBN)
            self.update_category_edit.setText(self.update_book.category)
            self.update_author_edit.setText(self.update_book.author)
            self.update_publisher_edit.setText(self.update_book.publisher)
            self.update_publication_date_edit.setDate(datetime.strptime(self.update_book.publication_date, "%Y.%m"))
            self.update_borrowed_by_select.setCurrentText("已出借" if self.update_book.borrowed_by != "" else "未出借")

            # 更新图片路径
            if self.update_book.img_url:
                fileName = os.path.join(self.config.CURRENT_PATH, self.update_book.img_url)
                pixmap = QPixmap(fileName)
                pixmap = pixmap.scaled(self.update_book_img.size(), Qt.IgnoreAspectRatio)
                self.update_book_img.setPixmap(pixmap)

        except Exception as e:
            module_logger.info(f"查询书籍失败{e}")
            self.messager.show_error(f"查询书籍失败{e}")

    def update_ok(self):
        """
        获取输入图书信息，根据 id 更新图书至数据库
        """
        try:
            self.update_book.book_id =  self.update_book_id_edit.text()
            self.update_book.book_name = self.update_book_name_edit.text()
            self.update_book.ISBN = self.update_ISBN_edit.text()
            self.update_book.category = self.update_category_edit.text()
            self.update_book.author = self.update_author_edit.text()
            self.update_book.publisher = self.update_publisher_edit.text()
            self.update_book.publication_date = self.update_publication_date_edit.text()
            self.update_book.borrowed_by = ("" if self.update_borrowed_by_select.currentText() == "未出借" else "admin")

            # 存入数据库
            (flag, msg) = ServicesHelper.get_update_service().update_book_by_id(self.update_book)
            if flag:
                self.messager.show_info(msg)
            else:
                self.messager.show_error(msg)

        except ValueError as e:
            module_logger.error(f"输入错误:{e}")
            self.messager.show_error(f"输入错误:{e}")

    def update_del(self):
        """
        根据 id 删除图书
        """
        self.update_book = Book()
        self.update_book_name_edit.setText("")
        self.update_ISBN_edit.setText("")
        self.update_category_edit.setText("")
        self.update_author_edit.setText("")
        self.update_publisher_edit.setText("")
        self.update_file_name.setText("")
        self.update_book_img.clear()

        # 删除
        (flag, msg) = ServicesHelper.get_update_service().del_book_by_id(self.update_book_id_edit.text())
        if flag:
            self.messager.show_info(msg)
        else:
            self.messager.show_error(msg)

    """#图书查询界面"""
    def query_books(self):
        """
        根据搜索的关键词，查找书籍并显示列表，此函数使用了多线程
        """
        keywords = self.query_search_keywords_edit.text()

        # """
        # 此处有个小 bug，当程序从管理员切换到普通用户时，不重启程序的情况下，
        # keywords 是空的，但执行了 if 里的语句，未知原因，可能是信号激发列表刷新的原因
        # """
        if len(keywords) < 2:
            module_logger.info("关键词长度必须大等于2")
            self.messager.show_info(f"关键词长度必须大等于2")
            return

        # 获取登录用户，用于将借阅的书籍置顶
        (flag, login_user) = ServicesHelper.get_role_service().get_current_user()
        user_id = login_user.user_id if flag else ""

        (flag, books) = ServicesHelper.get_query_service().get_books_by_keywords(keywords=keywords, user_id=user_id)
        if flag is False:
            msg = books
            self.messager.show_error(msg)
            return

        self.query_book_list = books
        # 清除视图列表
        self.book_list_widget.clear()

        # 创建 QListWidgetItem 添加到 book_detail_widget 容器形成列表，
        # 注意：过多返回数据会在这里造成轻微卡顿，并非线程原因，这里是主程序执行
        if self.query_book_list:
            for book in self.query_book_list:
                book_detail_widget = BookDetail(book, "user")
                item = QListWidgetItem(self.book_list_widget)
                item.setSizeHint(book_detail_widget.size())
                self.book_list_widget.addItem(item)
                self.book_list_widget.setItemWidget(item, book_detail_widget)
        else:
            self.messager.show_info(f"抱歉，没有找到与{keywords}相关的书籍")

    def query_rank(self):
        """
        对可见的所有书籍 self.query_book_list 进行排序，不从数据库获取，只对查询结果排序
        @return:
        """
        rank_mode = self.query_rank_select.currentText()

        (flag, books) = ServicesHelper.get_query_service().sort_books(self.query_book_list, rank_mode)
        if flag is False:
            msg = books
            self.messager.show_error(msg)
            return

        # 清空视图列表
        self.book_list_widget.clear()

        if books:
            for book in books:
                book_detail_widget = BookDetail(book, "user")
                item = QListWidgetItem(self.book_list_widget)
                item.setSizeHint(book_detail_widget.size())
                self.book_list_widget.addItem(item)
                self.book_list_widget.setItemWidget(item, book_detail_widget)

    """图书统计界面"""
    def stats_search(self):
        """
        有关关键词的书籍借阅情况
        匹配书籍的书名--------该函数使用了多线程
        """
        key_words = self.stats_keywords_edit.text()
        if len(key_words) < 2:
            module_logger.info("关键词长度必须大等于2")
            self.messager.show_info(f"关键词长度必须大等于2")
            return

        def get_data_func(data):
            """
             作为槽函数 传递给线程
             @param data: (flag, (all_num, borrowed_num, book_name_list))
             """
            module_logger.info("开始绘制")
            (flag, (all_num, borrowed_num, book_name_list)) = data
            print(data)

            if flag and all_num and book_name_list:
                self.figure_view_221.show()
                self.figure_view_222.hide()
                self.figure_view_223.show()
                self.figure_view_224.hide()

                self.fig_221.create_pie(x=[all_num - borrowed_num, borrowed_num], y=["未出借", "已出借"], title="相关书籍借阅情况")
                self.fig_223.create_clould(words=book_name_list, title="相关书籍名称图云(随机抽取)")
            else:
                self.messager.show_info(f"没有与{key_words}相关的书籍")
        # 启动线程
        ServicesHelper.get_stats_service().calculate_match_case_by_keywords_thresd(accept_data_func=get_data_func, keywords=key_words)

    def stats_show_all(self):
        """
        统计所有书籍信息，借阅情况，借阅热度榜，借阅历史，图书种类统计--------该函数使用了多线程
        """
        def get_user_case_data_func(data):
            """
             作为槽函数 传递给线程
             @param data:   (flag, (category_name_and_num, date_list, book_name_list))
             """
            module_logger.info("开始绘制")
            (flag, (category_name_and_num, date_list, book_name_list)) = data

            if flag:
                self.fig_221.create_pie(x=list(category_name_and_num.values()),
                                        y=list(category_name_and_num.keys()),
                                        title="图书分类情况")
                self.fig_223.create_scatter(x=date_list, y=book_name_list, title="图书借阅历史")

        def get_admin_case_data_func(data):
            """
            作为槽函数 传递给线程
            @param data: (flag, (category_name_and_num, all_num, borrowed_num, publication_date_and_num))
            """
            module_logger.info("开始绘制")
            (flag, data_tuple) = data

            if flag is False:
                self.messager.show_error(msg=data_tuple)
                return

            (category_name_and_num, all_num, borrowed_num, publication_date_and_num) = data_tuple
            self.fig_221.create_pie(x=[all_num - borrowed_num, borrowed_num],
                                    y=["未出借", "已出借"],
                                    title="图书借阅情况")

            self.fig_222.create_pie(x=list(category_name_and_num.values()),
                                    y=list(category_name_and_num.keys()),
                                    title="图书分类情况(数量靠前)")

            self.fig_223.create_bar(x=list(publication_date_and_num.keys()),
                                    y=list(publication_date_and_num.values()),
                                    title="出版年份统计(数量靠前)")

        # 用户访问统计---权限检查
        if ServicesHelper.get_role_service().check_permissions([34, 35, 36, 37])[0]:
            self.figure_view_221.show()
            self.figure_view_222.hide()
            self.figure_view_223.show()
            self.figure_view_224.hide()

            # 用于统计用户自身借阅情况
            (flag, user) = ServicesHelper.get_role_service().get_current_user()
            if flag:
                # 启动线程
                ServicesHelper.get_stats_service().calculate_user_case_thresd(accept_data_func=get_user_case_data_func,
                                                                              user_id=user.user_id)

        # 管理员访问统计---权限检查
        if ServicesHelper.get_role_service().check_permissions([31, 32, 33, 34, 35])[0]:
            self.figure_view_221.show()
            self.figure_view_222.show()
            self.figure_view_223.show()
            self.figure_view_224.hide()

            # 启动线程
            ServicesHelper.get_stats_service().calculate_admin_case_thresd(accept_data_func=get_admin_case_data_func)

    def stats_out_img(self):
        # 保存图表 PNG 格式到指定文件夹
        self.fig_221.graph_out()
        self.fig_222.graph_out()
        self.fig_223.graph_out()
        self.fig_224.graph_out()

    """#用户管理界面"""
    def user_update(self):
        """
        根据用户输入，在密码验证通过后，更新用户信息
        :return:
        """
        login_user = ServicesHelper.get_role_service().get_current_user()[1]
        user = copy.deepcopy(login_user)
        raw_role = user.role

        try:
            user.user_name = self.user_name_edit.text()
            user.user_id = self.user_id_edit.text()
            user.email = self.user_email_edit.text()
            user.role = self.user_role_select.currentText()
            old_password = self.user_password_edit.text()
            new_password = self.user_new_password_edit.text()

            # 验证密码
            if user.verify_password(old_password):
                user.password = new_password
            else:
                module_logger.info("密码错误，无法更新用户信息")
                self.messager.show_error("密码错误，无法更新用户信息")
                return

            # 数据库更新
            (flag, msg) = ServicesHelper.get_user_service().update_user_by_id(user=user)

            if flag:
                self.messager.show_info(msg)
            else:
                self.messager.show_error(msg)

            if flag and user.role != raw_role:
                self.messager.show_info("角色信息变化需要重新登录")
                # 数据清理
                self.book_list_widget.clear()
                self.update_book = Book()
                self.query_book_list = []
                self.fig_221.clear()
                self.fig_222.clear()
                self.fig_223.clear()
                self.fig_224.clear()
                # 隐藏画布
                self.figure_view_221.hide()
                self.figure_view_222.hide()
                self.figure_view_223.hide()
                self.figure_view_224.hide()
                # 通知主窗口跳转到登录页
                QTimer.singleShot(3000, lambda: q_signals.AdminToLogin_Signal.emit())

        except Exception as e:
            module_logger.info(f"更新用户信息错误：{e}")
            self.messager.show_error(f"更新用户信息错误：{e}")

    def user_del(self):
        """
        注销用户，根据id 从数据删除用户
        """
        login_user = ServicesHelper.get_role_service().get_current_user()[1]
        user_id = login_user.user_id

        (flag, msg) = ServicesHelper.get_user_service().del_user_by_id(user_id)
        if flag:
            self.messager.show_info(f"{msg}:即将进入登录页面")
            # 通知主窗口跳转到登录页
            QTimer.singleShot(3000, lambda: q_signals.AdminToLogin_Signal.emit())
        else:
            self.messager.show_error(msg)
